package com.chansos.dubbogo.ui;

import com.chansos.dubbogo.common.DatabaseDialect;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBPanel;
import com.intellij.ui.components.JBRadioButton;

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class ComponentsGroup extends JBPanel<ComponentsGroup> {
    public final static String componentModVersion = "v0.1.6";

    private boolean useWebService;
    private JTextField webServicePortText;

    private boolean useMemoryCache;
    private JTextField memoryCacheAddressText;
    private JTextField memoryCachePortText;
    private JTextField memoryCachePasswordText;
    private JTextField memoryCacheDefaultDBText;

    private boolean useDatabase;
    private final List<JBRadioButton> databaseOptions = new ArrayList<>() {{
        for (DatabaseDialect value : DatabaseDialect.values()) {
            JBRadioButton jbRadioButton = new JBRadioButton(value.getName(), value.isSelected());
            jbRadioButton.setToolTipText(value.getAlias());
            jbRadioButton.setPreferredSize(new Dimension(90, (int) jbRadioButton.getPreferredSize().getHeight()));
            add(jbRadioButton);
        }
    }};

    private JTextField databaseAddressText;
    private JTextField databasePortText;
    private JTextField databaseNameText;
    private JTextField databaseUsernameText;
    private JTextField databasePasswordText;

    public ComponentsGroup() {
        super(new FlowLayout(FlowLayout.LEFT));
        JBLabel label = new JBLabel("Middleware: ");
        label.setPreferredSize(new Dimension(100, (int) label.getPreferredSize().getHeight()));
        add(label);

        Box box = Box.createVerticalBox();

        addWebService(box);
        addRedisDriver(box);
        addDatabaseDriver(box);

        add(box);
    }

    private void addWebService(Box box) {
        JBPanel<?> panel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
        JBCheckBox checkBox = new JBCheckBox("Web service (Gin)");
        checkBox.setPreferredSize(new Dimension(170, (int) checkBox.getPreferredSize().getHeight()));
        panel.add(checkBox);

        Box innerBox = Box.createVerticalBox();

        JBPanel<?> infoPanel;
        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel portLabel = new JBLabel("Port");
            portLabel.setPreferredSize(new Dimension(60, (int) portLabel.getPreferredSize().getHeight()));
            infoPanel.add(portLabel);
            webServicePortText = new JTextField();
            webServicePortText.setDocument(new PortInputDocument(webServicePortText));
            webServicePortText.setPreferredSize(new Dimension(64, 28));
            webServicePortText.setText("8280");
            infoPanel.add(webServicePortText);
            innerBox.add(infoPanel);
        }

        innerBox.setVisible(false);
        checkBox.addChangeListener(e -> {
            boolean enable = checkBox.isSelected();
            ComponentsGroup.this.useWebService = enable;
            innerBox.setVisible(enable);
        });
        panel.add(innerBox);
        box.add(panel);
    }

    private void addRedisDriver(Box box) {
        JBPanel<?> panel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
        JBCheckBox checkBox = new JBCheckBox("Memory cache (Redis)");
        checkBox.setPreferredSize(new Dimension(170, (int) checkBox.getPreferredSize().getHeight()));
        panel.add(checkBox);

        Box innerBox = Box.createVerticalBox();
        innerBox.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.GRAY));

        JBPanel<?> infoPanel;
        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel addressLabel = new JBLabel("Address");
            addressLabel.setPreferredSize(new Dimension(60, (int) addressLabel.getPreferredSize().getHeight()));
            infoPanel.add(addressLabel);
            memoryCacheAddressText = new JTextField();
            memoryCacheAddressText.setPreferredSize(new Dimension(140, 28));
            memoryCacheAddressText.setText("127.0.0.1");
            infoPanel.add(memoryCacheAddressText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel portLabel = new JBLabel("Port");
            portLabel.setPreferredSize(new Dimension(60, (int) portLabel.getPreferredSize().getHeight()));
            infoPanel.add(portLabel);
            memoryCachePortText = new JTextField();
            memoryCachePortText.setDocument(new PortInputDocument(memoryCachePortText));
            memoryCachePortText.setPreferredSize(new Dimension(64, 28));
            memoryCachePortText.setText("6379");
            infoPanel.add(memoryCachePortText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel passwordLabel = new JBLabel("Password");
            passwordLabel.setPreferredSize(new Dimension(60, (int) passwordLabel.getPreferredSize().getHeight()));
            infoPanel.add(passwordLabel);
            memoryCachePasswordText = new JTextField();
            memoryCachePasswordText.setPreferredSize(new Dimension(140, 28));
            memoryCachePasswordText.setText("root");
            infoPanel.add(memoryCachePasswordText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel dbLabel = new JBLabel("DB");
            dbLabel.setPreferredSize(new Dimension(60, (int) dbLabel.getPreferredSize().getHeight()));
            infoPanel.add(dbLabel);
            memoryCacheDefaultDBText = new JTextField();
            memoryCacheDefaultDBText.setPreferredSize(new Dimension(64, 28));
            memoryCacheDefaultDBText.setDocument(new PortInputDocument(memoryCacheDefaultDBText, 0, 15));
            memoryCacheDefaultDBText.setText("0");
            infoPanel.add(memoryCacheDefaultDBText);
            innerBox.add(infoPanel);
        }

        innerBox.setVisible(false);
        checkBox.addChangeListener(e -> {
            boolean enable = checkBox.isSelected();
            ComponentsGroup.this.useMemoryCache = enable;
            innerBox.setVisible(enable);
        });

        panel.add(innerBox);
        box.add(panel);
    }

    private void addDatabaseDriver(Box box) {
        JBPanel<?> panel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
        JBCheckBox checkBox = new JBCheckBox("Database driver (Gorm)");
        checkBox.setPreferredSize(new Dimension(170, (int) checkBox.getPreferredSize().getHeight()));
        panel.add(checkBox);

        Box innerBox = Box.createVerticalBox();
        innerBox.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.GRAY));

        JBPanel<?> infoPanel;
        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel dialectLabel = new JBLabel("Dialect");
            dialectLabel.setPreferredSize(new Dimension(60, (int) dialectLabel.getPreferredSize().getHeight()));
            infoPanel.add(dialectLabel);

            Box optionBox = Box.createVerticalBox();
            JBPanel<?> optionPanel = null;
            int col = 0;
            ButtonGroup databaseDialectGroup = new ButtonGroup();
            for (JBRadioButton option : databaseOptions) {
                if (col % 2 == 0) {
                    optionPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
                    optionBox.add(optionPanel);
                }
                databaseDialectGroup.add(option);
                optionPanel.add(option);
                col++;
            }
            infoPanel.add(optionBox);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel addressLabel = new JBLabel("Address");
            addressLabel.setPreferredSize(new Dimension(60, (int) addressLabel.getPreferredSize().getHeight()));
            infoPanel.add(addressLabel);
            databaseAddressText = new JTextField();
            databaseAddressText.setPreferredSize(new Dimension(140, 28));
            databaseAddressText.setText("127.0.0.1");
            infoPanel.add(databaseAddressText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel portLabel = new JBLabel("Port");
            portLabel.setPreferredSize(new Dimension(60, (int) portLabel.getPreferredSize().getHeight()));
            infoPanel.add(portLabel);
            databasePortText = new JTextField();
            databasePortText.setDocument(new PortInputDocument(databasePortText));
            databasePortText.setPreferredSize(new Dimension(64, 28));
            databasePortText.setText("3306");
            infoPanel.add(databasePortText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel databaseLabel = new JBLabel("Database");
            databaseLabel.setPreferredSize(new Dimension(60, (int) databaseLabel.getPreferredSize().getHeight()));
            infoPanel.add(databaseLabel);
            databaseNameText = new JTextField();
            databaseNameText.setPreferredSize(new Dimension(140, 28));
            databaseNameText.setText("");
            infoPanel.add(databaseNameText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel usernameLabel = new JBLabel("Username");
            usernameLabel.setPreferredSize(new Dimension(60, (int) usernameLabel.getPreferredSize().getHeight()));
            infoPanel.add(usernameLabel);
            databaseUsernameText = new JTextField();
            databaseUsernameText.setPreferredSize(new Dimension(140, 28));
            databaseUsernameText.setText("root");
            infoPanel.add(databaseUsernameText);
            innerBox.add(infoPanel);
        }

        {
            infoPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT));
            JBLabel passwordLabel = new JBLabel("Password");
            passwordLabel.setPreferredSize(new Dimension(60, (int) passwordLabel.getPreferredSize().getHeight()));
            infoPanel.add(passwordLabel);
            databasePasswordText = new JTextField();
            databasePasswordText.setPreferredSize(new Dimension(140, 28));
            databasePasswordText.setText("root");
            infoPanel.add(databasePasswordText);
            innerBox.add(infoPanel);
        }

        innerBox.setVisible(false);
        checkBox.addChangeListener(e -> {
            boolean enable = checkBox.isSelected();
            ComponentsGroup.this.useDatabase = enable;
            innerBox.setVisible(enable);
        });

        panel.add(innerBox);
        box.add(panel);
    }

    static class PortInputDocument extends PlainDocument {
        JTextField textField;

        private int min = 1;
        private int max = 65535;

        public PortInputDocument(JTextField textField) {
            this.textField = textField;
        }

        public PortInputDocument(JTextField textField, int min, int max) {
            this.textField = textField;
            this.min = min;
            this.max = max;
        }

        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            if (textField.getText().length() >= 5) {
                return;
            }
            char[] source = str.toCharArray();
            char[] result = new char[source.length];
            int j = 0;

            for (int i = 0; i < result.length; i++) {
                if (Character.isDigit(source[i])) {
                    result[j++] = source[i];
                } else {
                    Toolkit.getDefaultToolkit().beep();
                }
            }
            super.insertString(offs, new String(result, 0, j), a);

            int port = Integer.parseInt(textField.getText());
            int port1 = Math.min(max, Math.max(min, port));
            if (port != port1) {
                textField.setText(String.valueOf(port1));
            }
        }
    }

    public boolean isUseWebService() {
        return useWebService;
    }

    public static class WebServiceConfig {
        private String port;

        public WebServiceConfig(String port) {
            this.port = port;
        }

        public String getPort() {
            return port;
        }

        public void setPort(String port) {
            this.port = port;
        }

        @Override
        public String toString() {
            return "server: # Web Service\n" + "  host: 0.0.0.0\n" + "  port: " + port + "\n";
        }

        public String mainImport() {
            return "webMiddleware \"gitee.com/changeden/dubbo-go-middleware-web/middleware\"";
        }

        public String mainSetupHookFunc() {
            return "func onWebMiddlewareSetup(router *gin.Engine) {\n" +
                    "\tcontroller.SetRouter(router)\n" +
                    "\tlogger.Info(\"web middleware setup\")\n" +
                    "}\n";
        }

        public String mainSetupHookStatement() {
            return "webMiddleware.NewWebSetupHook(onWebMiddlewareSetup),\n";
        }

        public String modImport() {
            return "gitee.com/changeden/dubbo-go-middleware-web " + componentModVersion + "\n";
        }
    }

    public WebServiceConfig getWebServiceConfig() {
        if (!isUseWebService()) {
            return null;
        }
        return new WebServiceConfig(webServicePortText.getText());
    }

    public boolean isUseMemoryCache() {
        return useMemoryCache;
    }

    public static class MemoryCacheConfig {
        private String address;
        private String port;
        private String password;
        private String defaultDB;

        public MemoryCacheConfig(String address, String port, String password, String defaultDB) {
            this.address = address;
            this.port = port;
            this.password = password;
            this.defaultDB = defaultDB;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getPort() {
            return port;
        }

        public void setPort(String port) {
            this.port = port;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDefaultDB() {
            return defaultDB;
        }

        public void setDefaultDB(String defaultDB) {
            this.defaultDB = defaultDB;
        }

        @Override
        public String toString() {
            return "redis: # Redis driver\n" + "  host: " + address + "\n" + "  port: " + port + "\n" + "  password: " + password + "\n" + "  defaultDb: " + defaultDB + "\n" + "  max_retries: 1\n";
        }

        public String mainImport() {
            return "redisMiddleware \"gitee.com/changeden/dubbo-go-middleware-redis/middleware\"";
        }

        public String mainSetupHookFunc() {
            return "func onRedisMiddlewareSetup() {\n" +
                    "\tlogger.Info(\"redis middleware setup\")\n" +
                    "}\n";
        }

        public String mainSetupHookStatement() {
            return "redisMiddleware.NewRedisSetupHook(onRedisMiddlewareSetup),\n";
        }

        public String modImport() {
            return "gitee.com/changeden/dubbo-go-middleware-redis " + componentModVersion + "\n";
        }
    }

    public MemoryCacheConfig getMemoryCacheConfig() {
        if (!isUseMemoryCache()) {
            return null;
        }
        return new MemoryCacheConfig(memoryCacheAddressText.getText(), memoryCachePortText.getText(), memoryCachePasswordText.getText(), memoryCacheDefaultDBText.getText());
    }

    public boolean isUseDatabase() {
        return useDatabase;
    }

    public static class DatabaseConfig {
        private String dialect;
        private String address;
        private String port;
        private String database;
        private String username;
        private String password;

        public DatabaseConfig(String dialect, String address, String port, String database, String username, String password) {
            this.dialect = dialect;
            this.address = address;
            this.port = port;
            this.database = database;
            this.username = username;
            this.password = password;
        }

        public String getDialect() {
            return dialect;
        }

        public void setDialect(String dialect) {
            this.dialect = dialect;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getPort() {
            return port;
        }

        public void setPort(String port) {
            this.port = port;
        }

        public String getDatabase() {
            return database;
        }

        public void setDatabase(String database) {
            this.database = database;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        @Override
        public String toString() {
            return "database: # Database driver\n" + "  dialect: " + dialect + "\n" + "  host: " + address + "\n" + "  port: " + port + "\n" + "  database: ${project-name}-db\n" + "  username: " + username + "\n" + "  password: " + password + "\n";
        }

        public String mainImport() {
            return "databaseMiddleware \"gitee.com/changeden/dubbo-go-middleware-database/middleware\"";
        }

        public String mainSetupHookFunc() {
            return "func onDatabaseMiddlewareSetup() {\n" +
                    "\tlogger.Info(\"database middleware setup\")\n" +
                    "}\n";
        }

        public String mainSetupHookStatement() {
            return "databaseMiddleware.NewDatabaseSetupHook(onDatabaseMiddlewareSetup),\n";
        }

        public String modImport() {
            return "gitee.com/changeden/dubbo-go-middleware-database " + componentModVersion + "\n";
        }
    }

    public DatabaseConfig getDatabaseConfig() {
        if (!isUseDatabase()) {
            return null;
        }
        String dialect;
        Optional<JBRadioButton> option = databaseOptions.stream().filter(AbstractButton::isSelected).findFirst();
        if (option.isPresent()) {
            dialect = option.get().getToolTipText();
        } else {
            dialect = DatabaseDialect.Mysql.getAlias();
        }
        return new DatabaseConfig(dialect, databaseAddressText.getText(), databasePortText.getText(), databaseNameText.getText(), databaseUsernameText.getText(), databasePasswordText.getText());
    }

    public static class ComponentConfigs {
        private WebServiceConfig webServiceConfig;
        private MemoryCacheConfig memoryCacheConfig;
        private DatabaseConfig databaseConfig;

        public ComponentConfigs(WebServiceConfig webServiceConfig, MemoryCacheConfig memoryCacheConfig, DatabaseConfig databaseConfig) {
            this.webServiceConfig = webServiceConfig;
            this.memoryCacheConfig = memoryCacheConfig;
            this.databaseConfig = databaseConfig;
        }

        public WebServiceConfig getWebServiceConfig() {
            return webServiceConfig;
        }

        public void setWebServiceConfig(WebServiceConfig webServiceConfig) {
            this.webServiceConfig = webServiceConfig;
        }

        public MemoryCacheConfig getMemoryCacheConfig() {
            return memoryCacheConfig;
        }

        public void setMemoryCacheConfig(MemoryCacheConfig memoryCacheConfig) {
            this.memoryCacheConfig = memoryCacheConfig;
        }

        public DatabaseConfig getDatabaseConfig() {
            return databaseConfig;
        }

        public void setDatabaseConfig(DatabaseConfig databaseConfig) {
            this.databaseConfig = databaseConfig;
        }
    }

    public ComponentConfigs getComponentConfigs() {
        return new ComponentConfigs(getWebServiceConfig(), getMemoryCacheConfig(), getDatabaseConfig());
    }
}
